package net.gdface.utils;

import static net.gdface.utils.ConditionChecks.checkNotNull;

import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Map.Entry;

/**
 * 杂项(未分类)工具箱
 * @author guyadong
 *
 */
public class MiscellaneousUtils {
	/**
	 * 将{@code URL}字符串转换为{@code URI}对象<br>
	 * 在转换过程中会将自动对不符合URI规范的字符进行编码,<br>
	 * 在转换过程中先从字符串生成{@code URL}对象,如果{@code String}不能转换成URL对象，则抛出异常
	 * @param urlStr
	 * @return {@code URI}对象
	 * @throws MalformedURLException
	 */
	public static final URI createURI(String urlStr) throws MalformedURLException{
		try {
			return new URI(urlStr);
		} catch (URISyntaxException e) {
			try {
				URL url=new URL(urlStr);
				return new URI(url.getProtocol(),url.getUserInfo(),url.getHost(),url.getPort(),url.getPath(),url.getQuery(),url.getRef());
			} catch (URISyntaxException e1) {
				throw new RuntimeException(e1);
			}
		}
	}
	/**
	 * 对{@link Map}中元素以key排序后，每行以{key}={value}形式输出到{@link Writer}<br>
	 * map为空或null时则不向writer写入任何内容
	 * @param map
	 * @param writer 为null抛出{@link IllegalArgumentException}
	 * @param lineSeparator 换行符,为null则使用系统默认的换行符(windows \n linux \r\n)
	 * @throws IOException
	 */
	public static  final void storeSortedMap(Map<String,String> map,Writer writer, String lineSeparator)  throws IOException {
		Assert.notNull(writer, "writer");
		TreeMap<String, String> sortedMap = new TreeMap<String,String>();
		if(null!=map){
			sortedMap.putAll(map);
		}
		BufferedWriter bw=(writer instanceof BufferedWriter)?(BufferedWriter)writer
				: new BufferedWriter(writer);
		for (Entry<String,String> e:sortedMap.entrySet()) {
			bw.write(e.getKey() + "=" + e.getValue());
			if(null==lineSeparator){
				bw.newLine();
			}else{
				bw.write("\n");
			}
		}
		bw.flush();
	}

	/**
	 * 对 {@link Collection}中元素排序后(去除重复)，元素分行输出到{@link Writer}<br>
	 * collection为空或null时则不向writer写入任何内容
	 * @param collection
	 * @param writer 为null抛出{@link IllegalArgumentException}
	 * @param lineSeparator 换行符,为null则使用系统默认的换行符(windows \n linux \r\n)
	 * @throws IOException
	 */
	public static final void storeSortedSet(Collection<String> collection,Writer writer, String lineSeparator)  throws IOException {
		Assert.notNull(writer, "writer");
		TreeSet<String> sortedSet = new TreeSet<String>();
		if(null!=collection){
			sortedSet.addAll(collection);
		}
		BufferedWriter bw=(writer instanceof BufferedWriter)?(BufferedWriter)writer
				: new BufferedWriter(writer);
		for (String e:sortedSet) {			
			bw.write(e);
			if(null==lineSeparator){
				bw.newLine();
			}else{
				bw.write("\n");
			}
		}
		bw.flush();
	}
	
	/**
	 * 比较两个Map是否相等
	 * @param m1
	 * @param m2
	 * @return 相等返回{@code true},否则返回{@code false}
	 */
	public static final <K,V>boolean equals(Map<K,V> m1,Map<K,V> m2){
		if(m1==m2){
			return true;
		}
		if(null ==m1 || null ==m2){
			return false;
		}
		if(m1.size() != m2.size()){
			return false;
		}
		for(Entry<K, V> entry:m1.entrySet()){
			K key = entry.getKey();
			if(!m2.containsKey(key)){
				return false;
			}
			V v1 = entry.getValue();
			V v2 = m2.get(key);
			if(v1 ==v2 ) {
				continue;
			}
			if(null ==v1 || null ==v2){
				return false;
			}
			if(!v1.equals(v2)){
				return false;
			}
		}
		return true;
	}
	/**
	 * 将{@code input}用{@code delim}指定的分隔符切分为不含空格和分隔符的一组字符串
	 * @param input 输入字符串
	 * @param delim 包含多个分隔符的字符串
	 * @return {@code input}或{@code delim}为{@code null}时返回空表
	 */
	public static final List<String> elementsOf(String input,String delim) {
		List<String> list = new ArrayList<String>();
		if (input != null && delim != null) {
			StringTokenizer st = new StringTokenizer(input, delim);
			while (st.hasMoreTokens()) {
				list.add(st.nextToken());
			}
		}
		return list;
	}
	/**
	 * 将{@code input}用分隔符{@code ;,\t\r\f\n}切分为不含空格和分隔符的一组字符串
	 * @param input
	 * @return {@code input}为{@code null}时返回空表
	 */
	public static final List<String> elementsOf(String input) {
		return elementsOf(input," ,;\t\n\r\f");
	}
	/**
	 * 根据class path 字符串创建 {@link URLClassLoader}实例
	 * @param classpath class path 字符串
	 * @return URLClassLoader 实例, {@code classpath} 为空或{@code null}或{@code classpath}解析出的元素为空则返回{@code null}
	 */
	public static final URLClassLoader buildClassLoader(String classpath){
		if(classpath != null && classpath.length() > 0){
			classpath = normalizeClasspath(classpath);
			List<String> paths = elementsOf(classpath, File.pathSeparator + "\t\n\r\f");
			ArrayList<URL> urls = new ArrayList<URL>(paths.size());
			for(String path:paths){
				try {
					urls.add(new File(path).toURI().toURL());
				} catch (MalformedURLException e) {
					// DO NOTHING
				}
			}
			if(!urls.isEmpty()){
				return new URLClassLoader(urls.toArray(new URL[urls.size()]));
			}
		}
		return null;
	}
	/**
	 * 归一化 class path字符串,将','转为指定平台的路径分隔符
	 * @param classpath
	 * @return 归一化后的 class path 字符串
	 */
	public static final String normalizeClasspath(String classpath){
		if( classpath != null && classpath.length() > 0 ){
			// 允许 , 做分隔符
			classpath = classpath.replaceAll(",", File.pathSeparator);
			// linux下允许用 ;号做分隔符
			if(File.pathSeparatorChar  != ';'){
				classpath = classpath.replaceAll(";", File.pathSeparator);
			}
		}
		return classpath;
	}
	/**
	 * 将指定的类名列表加载为类返回类列表,
	 * @param classNames 类名列表
	 * @param classpath 类加载路径class path,可为{@code null}
	 * @param initialize 是否初始化类
	 * @return 类列表 {@code classNames}为{@code null}返回空表
	 * @throws ClassNotFoundException
	 */
	public static final List<Class<?>> loadClasses(Iterable<String> classNames,String classpath, boolean initialize) throws ClassNotFoundException{
		LinkedList<Class<?>> classes = new LinkedList<Class<?>>();
		if(classNames != null){
			for(String classname:classNames){
				try {
					if(initialize){
						classes.add(Class.forName(classname));
					}else{
						classes.add(Class.forName(classname,initialize,MiscellaneousUtils.class.getClassLoader()));
					}
				} catch (ClassNotFoundException e) {
					ClassLoader classLoader = buildClassLoader(classpath);
					if(classLoader!= null){
						try {
							classes.add(Class.forName(classname, initialize, classLoader));
							continue;
						} catch (ClassNotFoundException e1) {
						}
					}
					throw e;
				}
			}
		}
		return classes;
	}
	/**
	 * 将指定的类名列表加载为类返回类列表,
	 * @param classNames 类名列表
	 * @param classpath 类加载路径class path,可为{@code null}
	 * @return 类列表 {@code classNames}为{@code null}返回空表
	 * @throws ClassNotFoundException
	 */
	public static final List<Class<?>> loadClasses(Iterable<String> classNames,String classpath) throws ClassNotFoundException{
		return loadClasses(classNames,classpath,true);
	}
	/**
	 * 将指定的类名列表加载为类返回类列表,
	 * @param classNames {@code ;,}或空格'分隔类名列表
	 * @param classpath 类加载路径class path,可为{@code null}
	 * @param initialize 是否初始化类
	 * @return 类列表 {@code classNames}为{@code null}返回空表
	 * @throws ClassNotFoundException
	 */
	public static final List<Class<?>> loadClasses(String classNames,String classpath, boolean initialize) throws ClassNotFoundException{
		return loadClasses(elementsOf(classNames), classpath, initialize);
	}
	
	/**
	 * 将指定的类名列表加载为类返回类列表,
	 * @param classNames {@code ;,}或空格'分隔类名列表
	 * @param classpath 类加载路径class path,可为{@code null}
	 * @return 类列表 {@code classNames}为{@code null}返回空表
	 * @throws ClassNotFoundException
	 */
	public static final List<Class<?>> loadClasses(String classNames,String classpath) throws ClassNotFoundException{
		return loadClasses(classNames, classpath, true);
	}
	
	/**
	 * 根据指定的参数返回,查找类的构造方法<br>
	 * 该方法会称尝试调用{@link Class#getConstructor(Class...)}查找,如果成功则返回构造器对象,
	 * 否则会遍历类所有公开构造器,使用{@link Class#isAssignableFrom(Class)}来对参数类型比较,
	 * 返回参数类型匹配父类的构造器对象,如果还是找不到则抛出异常
	 * @param clazz
	 * @param args 构造方法参数
	 * @return 构造方法
	 * @throws NoSuchMethodException 找不到匹配的构造方法则抛出异常
	 */
	@SuppressWarnings("unchecked")
	public static <T>Constructor<T> getConstructor(Class<T> clazz,Class<?> ...args) throws NoSuchMethodException
	{
		checkNotNull(clazz,"clazz is null");
		checkNotNull(args,"args is null");
		for(int i = 0; i < args.length; i++){			
			checkNotNull(args[i],"args["+i+"] is null");
		}
		try {			
			return clazz.getConstructor(args);
		} catch (NoSuchMethodException e) {
			for(Constructor<?> ctor:clazz.getConstructors()){
				Class<?>[] parameterTypes = ctor.getParameterTypes();
				if(args.length == parameterTypes.length){
					boolean matched = true;
					for(int i=0; i<args.length; ++i){
						if(!parameterTypes[i].isAssignableFrom(args[i])){
							matched = false;
							break;
						}
					}
					if(matched){
						return (Constructor<T>) ctor;
					}
				}
			}
			throw new NoSuchMethodException(clazz.getName() + ".<init>" + argumentTypesToString(args));
		}
	}
    private static String   argumentTypesToString(Class<?>[] argTypes) {
        StringBuilder buf = new StringBuilder();
        buf.append("(");
        if (argTypes != null) {
            for (int i = 0; i < argTypes.length; i++) {
                if (i > 0) {
                    buf.append(", ");
                }
                Class<?> c = argTypes[i];
                buf.append((c == null) ? "null" : c.getName());
            }
        }
        buf.append(")");
        return buf.toString();
    }
	/**
	 * 返回折叠字符串
	 * @param input
	 * @param limit
	 * @return 当input长度大于limit,且大于6字符时输出折叠显示的长度为limit字符串缩略部分以'...'代替,否则返回input
	 */
	public static String fold(String input,int limit){
		if(input != null && input.length() > 6 && input.length() > limit){
			int leftIndex = (limit - 3 + 1)/2;
			String left = input.substring(0, leftIndex);
			int rightIndex= input.length() - ((limit - 3)/2);
			String right = input.substring(rightIndex);
			return left + "..." + right;
		}
		return input;
	}
}
